1 module d_snprintf.snprintf;
2 
3 /* $Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp $ */
4 
5 /*
6 * Copyright (c) 1995 Patrick Powell.
7 *
8 * This code is based on code written by Patrick Powell <papowell@astart.com>.
9 * It may be used for any purpose as long as this notice remains intact on all
10 * source code distributions.
11 */
12 
13 /*
14 * Copyright (c) 2008 Holger Weiss.
15 *
16 * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
17 * My changes to the code may freely be used, modified and/or redistributed for
18 * any purpose.  It would be nice if additions and fixes to this file (including
19 * trivial code cleanups) would be sent back in order to let me include them in
20 * the version available at <http://www.jhweiss.de/software/snprintf.html>.
21 * However, this is not a requirement for using or redistributing (possibly
22 * modified) versions of this file, nor is leaving this notice intact mandatory.
23 */
24 
25 /*
26 * Copyright (c) 2018 KytoDragon.
27 *
28 * This version of the code is a port to the D programming language.
29 * Use however you like, as long as this and the previous notices remain intact.
30 */
31 
32 /*
33  * History
34  *
35  * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
36  *
37  *  Fixed the detection of infinite floating point values on IRIX (and
38  *  possibly other systems) and applied another few minor cleanups.
39  *
40  * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
41  *
42  *  Added a lot of new features, fixed many bugs, and incorporated various
43  *  improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
44  *  <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
45  *  <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
46  *  projects.  The additions include: support the "e", "E", "g", "G", and
47  *  "F" conversion specifiers (and use conversion style "f" or "F" for the
48  *  still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
49  *  "t", and "z" length modifiers; support the "#" flag and the (non-C99)
50  *  "'" flag; use localeconv(3) (if available) to get both the current
51  *  locale's decimal point character and the separator between groups of
52  *  digits; fix the handling of various corner cases of field width and
53  *  precision specifications; fix various floating point conversion bugs;
54  *  handle infinite and NaN floating point values; don't attempt to write to
55  *  the output buffer (which may be NULL) if a size of zero was specified;
56  *  check for integer overflow of the field width, precision, and return
57  *  values and during the floating point conversion; use the OUTCHAR() macro
58  *  instead of a function for better performance; provide asprintf(3) and
59  *  vasprintf(3) functions; add new test cases.  The replacement functions
60  *  have been renamed to use an "rpl_" prefix, the function calls in the
61  *  main project (and in this file) must be redefined accordingly for each
62  *  replacement function which is needed (by using Autoconf or other means).
63  *  Various other minor improvements have been applied and the coding style
64  *  was cleaned up for consistency.
65  *
66  * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
67  *
68  *  C99 compliant snprintf(3) and vsnprintf(3) functions return the number
69  *  of characters that would have been written to a sufficiently sized
70  *  buffer (excluding the '\0').  The original code simply returned the
71  *  length of the resulting output string, so that's been fixed.
72  *
73  * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
74  *
75  *  The original code assumed that both snprintf(3) and vsnprintf(3) were
76  *  missing.  Some systems only have snprintf(3) but not vsnprintf(3), so
77  *  the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
78  *
79  * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
80  *
81  *  The PGP code was using unsigned hexadecimal formats.  Unfortunately,
82  *  unsigned formats simply didn't work.
83  *
84  * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
85  *
86  *  Ok, added some minimal floating point support, which means this probably
87  *  requires libm on most operating systems.  Don't yet support the exponent
88  *  (e,E) and sigfig (g,G).  Also, fmtint() was pretty badly broken, it just
89  *  wasn't being exercised in ways which showed it, so that's been fixed.
90  *  Also, formatted the code to Mutt conventions, and removed dead code left
91  *  over from the original.  Also, there is now a builtin-test, run with:
92  *  gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
93  *
94  * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
95  *
96  *  This was ugly.  It is still ugly.  I opted out of floating point
97  *  numbers, but the formatter understands just about everything from the
98  *  normal C string format, at least as far as I can tell from the Solaris
99  *  2.5 printf(3S) man page.
100  */
101 
102 /*
103  * ToDo
104  *
105  * - Add wide character support.
106  * - Add support for "%a" and "%A" conversions.
107  * - Create test routines which predefine the expected results.  Our test cases
108  *   usually expose bugs in system implementations rather than in ours :-)
109  */
110 
111 import d_snprintf.vararg;
112 
113 alias snprintf = rpl_snprintf;
114 alias vsnprintf = rpl_vsnprintf!dummy_file_func;
115 
116 alias snprintf_file_func = void function(void* file, ubyte[] data) nothrow @nogc;
117 alias snprintf_alloc_func = void* function(size_t size) nothrow @nogc;
118 
119 private:
120 nothrow:
121 @nogc:
122 
123 /* Support for uintmax_t.  We also need UINTMAX_MAX. */
124 alias uintmax_t = ulong;
125 /* Support for intmax_t. */
126 alias intmax_t = long;
127 /* Support for uintptr_t. */
128 alias uintptr_t = size_t;
129 /*
130  * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
131  * 7.19.6.1, 7).  However, we'll simply use PTRDIFF_T and convert it to an
132  * unsigned type if necessary.  This should work just fine in practice.
133  */
134 alias uptrdiff_t = size_t;
135 /*
136  * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
137  * However, we'll simply use size_t and convert it to a signed type if
138  * necessary.  This should work just fine in practice.
139  */
140 alias ssize_t = ptrdiff_t;
141 
142 enum UINTMAX_MAX = ulong.max;
143 enum INT_MAX = int.max;
144 
145 /*
146 * Buffer size to hold the octal string representation of UINT128_MAX without
147 * nul-termination ("3777777777777777777777777777777777777777777").
148 */
149 enum MAX_CONVERT_LENGTH      = 43;
150 
151 /* Format read states. */
152 enum PRINT_S {
153     DEFAULT         = 0,
154     FLAGS           = 1,
155     WIDTH           = 2,
156     DOT             = 3,
157     PRECISION       = 4,
158     MOD             = 5,
159     CONV            = 6,
160 }
161 
162 /* Format flags. */
163 enum PRINT_F {
164     MINUS           = (1 << 0),
165     PLUS            = (1 << 1),
166     SPACE           = (1 << 2),
167     NUM             = (1 << 3),
168     ZERO            = (1 << 4),
169     QUOTE           = (1 << 5),
170     UP              = (1 << 6),
171     UNSIGNED        = (1 << 7),
172     TYPE_G          = (1 << 8),
173     TYPE_E          = (1 << 9),
174 }
175 
176 /* Conversion flags. */
177 enum PRINT_C {
178     CHAR            = 1,
179     SHORT           = 2,
180     LONG            = 3,
181     LLONG           = 4,
182     LDOUBLE         = 5,
183     SIZE            = 6,
184     PTRDIFF         = 7,
185     INTMAX          = 8,
186 }
187 
188 // We can't pass null as a template parameter of type function, so we use this as a comparison
189 void dummy_file_func(void* file, ubyte[] data) {}
190 
191 pragma(inline, true) T MAX(T)(T x, T y) { return ((x >= y) ? x : y); }
192 pragma(inline, true) char CHARTOINT(char ch) { return cast(char)(ch - '0'); }
193 pragma(inline, true) bool ISDIGIT(char ch) { return ('0' <= ch && ch <= '9'); }
194 pragma(inline, true) bool ISNAN(real x) { return (x != x); }
195 pragma(inline, true) bool ISINF(real x) { return (x != 0.0 && x + x == x); }
196 
197 int rpl_vsnprintf(alias file_func)(char[] str, string format, va_list args, void* file = null) {
198     size_t len = 0;
199     int overflow = 0;
200     int base = 0;
201     int cflags = 0;
202     int flags = 0;
203     int width = 0;
204     int precision = -1;
205     int state = PRINT_S.DEFAULT;
206     size_t format_index = 0;
207     size_t file_len = 0;
208     
209     pragma(inline, true) void OUTCHAR(char ch) {
210         if (len < str.length)
211             str[len] = ch;
212         len++;
213         static if (&file_func != &dummy_file_func) {
214             if (file != null && len == str.length) {
215                 file_func(file, cast(ubyte[])str);
216                 file_len += len;
217                 len = 0;
218             }
219         }
220     }
221 
222     /*
223     * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
224     * pointer." (7.19.6.5, 2)  We're forgiving and allow a null pointer
225     * even if a size larger than zero was specified.  At least NetBSD's
226     * snprintf(3) does the same, as well as other versions of this file.
227     * (Though some of these versions will write to a non-null buffer even
228     * if a size of zero was specified, which violates the standard.)
229     */
230     if (str is null && str.length != 0)
231         str = str[0..0];
232 
233     big_loop:
234     while (format_index < format.length) {
235         char ch = format[format_index++];
236         final switch (state) {
237             case PRINT_S.DEFAULT:
238                 if (ch == '%')
239                     state = PRINT_S.FLAGS;
240                 else
241                     OUTCHAR(ch);
242                 break;
243             case PRINT_S.FLAGS:
244                 switch (ch) {
245                     case '-':
246                         flags |= PRINT_F.MINUS;
247                         break;
248                     case '+':
249                         flags |= PRINT_F.PLUS;
250                         break;
251                     case ' ':
252                         flags |= PRINT_F.SPACE;
253                         break;
254                     case '#':
255                         flags |= PRINT_F.NUM;
256                         break;
257                     case '0':
258                         flags |= PRINT_F.ZERO;
259                         break;
260                     case '\'':    /* SUSv2 flag (not in C99). */
261                         flags |= PRINT_F.QUOTE;
262                         break;
263                     default:
264                         state = PRINT_S.WIDTH;
265                         format_index--;
266                         break;
267                 }
268                 break;
269             case PRINT_S.WIDTH:
270                 if (ISDIGIT(ch)) {
271                     ch = CHARTOINT(ch);
272                     if (width > (INT_MAX - ch) / 10) {
273                         overflow = 1;
274                         break big_loop;
275                     }
276                     width = 10 * width + ch;
277                 } else if (ch == '*') {
278                     /*
279                     * C99 says: "A negative field width argument is
280                     * taken as a `-' flag followed by a positive
281                     * field width." (7.19.6.1, 5)
282                     */
283                     width = cast(int)get_any_int(args);
284                     if (width < 0) {
285                         flags |= PRINT_F.MINUS;
286                         width = -width;
287                     }
288                     state = PRINT_S.DOT;
289                 } else {
290                     format_index--;
291                     state = PRINT_S.DOT;
292                 }
293                 break;
294             case PRINT_S.DOT:
295                 if (ch == '.') {
296                     state = PRINT_S.PRECISION;
297                 } else {
298                     format_index--;
299                     state = PRINT_S.MOD;
300                 }
301                 break;
302             case PRINT_S.PRECISION:
303                 if (precision == -1)
304                     precision = 0;
305                 
306                 if (ISDIGIT(ch)) {
307                     ch = CHARTOINT(ch);
308                     if (precision > (INT_MAX - ch) / 10) {
309                         overflow = 1;
310                         break big_loop;
311                     }
312                     precision = 10 * precision + ch;
313                 } else if (ch == '*') {
314                     /*
315                     * C99 says: "A negative precision argument is
316                     * taken as if the precision were omitted."
317                     * (7.19.6.1, 5)
318                     */
319                     precision = cast(int)get_any_int(args);
320                     if (precision < 0)
321                         precision = -1;
322                     state = PRINT_S.MOD;
323                 } else {
324                     format_index--;
325                     state = PRINT_S.MOD;
326                 }
327                 break;
328             case PRINT_S.MOD:
329                 switch (ch) {
330                     case 'h':
331                         if (format_index < format.length && format[format_index] == 'h') {
332                             /* It's a char. */
333                             format_index++;
334                             cflags = PRINT_C.CHAR;
335                         } else
336                             cflags = PRINT_C.SHORT;
337                         break;
338                     case 'l':
339                         if (format_index < format.length && format[format_index] == 'l') {
340                             /* It's a long long. */
341                             format_index++;
342                             cflags = PRINT_C.LLONG;
343                         } else
344                             cflags = PRINT_C.LONG;
345                         break;
346                     case 'L':
347                         cflags = PRINT_C.LDOUBLE;
348                         break;
349                     case 'j':
350                         cflags = PRINT_C.INTMAX;
351                         break;
352                     case 't':
353                         cflags = PRINT_C.PTRDIFF;
354                         break;
355                     case 'z':
356                         cflags = PRINT_C.SIZE;
357                         break;
358                     default:
359                         format_index--;
360                         break;
361                 }
362                 state = PRINT_S.CONV;
363                 break;
364             case PRINT_S.CONV:
365                 switch (ch) {
366                     case 'd':
367                         /* FALLTHROUGH */
368                     case 'i':
369                         intmax_t value;
370                         switch (cflags) {
371                             case PRINT_C.CHAR:
372                                 value = cast(char)get_any_int(args);
373                                 break;
374                             case PRINT_C.SHORT:
375                                 value = cast(short)get_any_int(args);
376                                 break;
377                             case PRINT_C.LONG:
378                                 value = cast(int)get_any_int(args);
379                                 break;
380                             case PRINT_C.LLONG:
381                             case PRINT_C.INTMAX:
382                                 value = cast(long)get_any_int(args);
383                                 break;
384                             case PRINT_C.SIZE:
385                             case PRINT_C.PTRDIFF:
386                                 value = cast(ptrdiff_t)get_any_int(args);
387                                 break;
388                             default:
389                                 value = get_any_int(args);
390                                 break;
391                         }
392                         fmtint!file_func(str, &len, value, 10, width,
393                             precision, flags, file, &file_len);
394                         break;
395                     case 'X':
396                         flags |= PRINT_F.UP;
397                         /* FALLTHROUGH */
398                         goto case 'x';
399                     case 'x':
400                         base = 16;
401                         /* FALLTHROUGH */
402                         goto case 'o';
403                     case 'o':
404                         if (base == 0)
405                             base = 8;
406                         /* FALLTHROUGH */
407                         goto case 'u';
408                     case 'u':
409                         if (base == 0)
410                             base = 10;
411                         uintmax_t value;
412                         flags |= PRINT_F.UNSIGNED;
413                         switch (cflags) {
414                             case PRINT_C.CHAR:
415                                 value = cast(ubyte)get_any_int(args);
416                                 break;
417                             case PRINT_C.SHORT:
418                                 value = cast(ushort)get_any_int(args);
419                                 break;
420                             case PRINT_C.LONG:
421                                 value = cast(uint)get_any_int(args);
422                                 break;
423                             case PRINT_C.LLONG:
424                             case PRINT_C.INTMAX:
425                                 value = cast(ulong)get_any_int(args);
426                                 break;
427                             case PRINT_C.SIZE:
428                             case PRINT_C.PTRDIFF:
429                                 value = cast(size_t)get_any_int(args);
430                                 break;
431                             default:
432                                 value = cast(uint)get_any_int(args);
433                                 break;
434                         }
435                         fmtint!file_func(str, &len, value, base, width,
436                             precision, flags, file, &file_len);
437                         break;
438                     case 'A':
439                         /* Not yet supported, we'll use "%F". */
440                         /* FALLTHROUGH */
441                         goto case 'F';
442                     case 'F':
443                         flags |= PRINT_F.UP;
444                         /* FALLTHROUGH */
445                         goto case 'a';
446                     case 'a':
447                         /* Not yet supported, we'll use "%f". */
448                         /* FALLTHROUGH */
449                         goto case 'f';
450                     case 'f':
451                         real fvalue;
452                         if (cflags == PRINT_C.LDOUBLE)
453                             fvalue = cast(real)get_any_float(args);
454                             // Unlike C, D does not upconvert floats to double. You need to use %lf to print doubles.
455                         else if (cflags == PRINT_C.LONG)
456                             fvalue = cast(double)get_any_float(args);
457                         else
458                             fvalue = cast(float)get_any_float(args);
459                         fmtflt!file_func(str, &len, fvalue, width,
460                             precision, flags, &overflow, file, &file_len);
461                         if (overflow)
462                             break big_loop;
463                         break;
464                     case 'E':
465                         flags |= PRINT_F.UP;
466                         /* FALLTHROUGH */
467                         goto case 'e';
468                     case 'e':
469                         real fvalue;
470                         flags |= PRINT_F.TYPE_E;
471                         if (cflags == PRINT_C.LDOUBLE)
472                             fvalue = cast(real)get_any_float(args);
473                         else if (cflags == PRINT_C.LONG)
474                             fvalue = cast(double)get_any_float(args);
475                         else
476                             fvalue = cast(float)get_any_float(args);
477                         fmtflt!file_func(str, &len, fvalue, width,
478                             precision, flags, &overflow, file, &file_len);
479                         if (overflow)
480                             break big_loop;
481                         break;
482                     case 'G':
483                         flags |= PRINT_F.UP;
484                         /* FALLTHROUGH */
485                         goto case 'g';
486                     case 'g':
487                         real fvalue;
488                         flags |= PRINT_F.TYPE_G;
489                         if (cflags == PRINT_C.LDOUBLE)
490                             fvalue = cast(real)get_any_float(args);
491                         else if (cflags == PRINT_C.LONG)
492                             fvalue = cast(double)get_any_float(args);
493                         else
494                             fvalue = cast(float)get_any_float(args);
495                         /*
496                         * If the precision is zero, it is treated as
497                         * one (cf. C99: 7.19.6.1, 8).
498                         */
499                         if (precision == 0)
500                             precision = 1;
501                         fmtflt!file_func(str, &len, fvalue, width,
502                             precision, flags, &overflow, file, &file_len);
503                         if (overflow)
504                             break big_loop;
505                         break;
506                     case 'c':
507                         char cvalue;
508                         if (va_get_type(args) is va_get_type!(char)) {
509                             cvalue = va_arg!(char)(args);
510                         } else {
511                             cvalue = cast(char)get_any_int(args);
512                         }
513                         OUTCHAR(cvalue);
514                         break;
515                     case 's':
516                         if (has_string_like_value(args)) {
517                             string strvalue = va_arg!(string)(args);
518                             if (precision == -1 || precision > strvalue.length) {
519                                 precision = cast(int)strvalue.length;
520                             }
521                             fmtstr!file_func(str, &len, strvalue.ptr, width,
522                                 precision, flags, file, &file_len);
523                         } else {
524                             const(char)* strvalue = cast(const(char)*)get_any_pointer(args);
525                             fmtstr!file_func(str, &len, strvalue, width,
526                                 precision, flags, file, &file_len);
527                         }
528                         break;
529                     case 'p':
530                         /*
531                         * C99 says: "The value of the pointer is
532                         * converted to a sequence of printing
533                         * characters, in an implementation-defined
534                         * manner." (C99: 7.19.6.1, 8)
535                         */
536                         void *strvalue = get_any_pointer(args);
537                         if (strvalue == null)
538                             /*
539                             * We use the glibc format.  BSD prints
540                             * "0x0", SysV "0".
541                             */
542                             fmtstr!file_func(str, &len, "(nil)", width, -1, flags, file, &file_len);
543                         else {
544                             /*
545                             * We use the BSD/glibc format.  SysV
546                             * omits the "0x" prefix (which we emit
547                             * using the PRINT_F.NUM flag).
548                             */
549                             flags |= PRINT_F.NUM;
550                             flags |= PRINT_F.UNSIGNED;
551                             fmtint!file_func(str, &len,
552                                 cast(uintptr_t)strvalue, 16, width,
553                                 precision, flags, file, &file_len);
554                         }
555                         break;
556                     case 'n':
557                         switch (cflags) {
558                             case PRINT_C.CHAR:
559                                 char* charptr = cast(char*)get_any_pointer(args);
560                                 *charptr = cast(char)(len + file_len);
561                                 break;
562                             case PRINT_C.SHORT:
563                                 short* shortptr = cast(short *)get_any_pointer(args);
564                                 *shortptr = cast(short)(len + file_len);
565                                 break;
566                             case PRINT_C.LONG:
567                                 int* longptr = cast(int *)get_any_pointer(args);
568                                 *longptr = cast(int)(len + file_len);
569                                 break;
570                             case PRINT_C.LLONG:
571                                 long* llongptr = cast(long *)get_any_pointer(args);
572                                 *llongptr = cast(long)(len + file_len);
573                                 break;
574                             case PRINT_C.SIZE:
575                                 /*
576                                 * C99 says that with the "z" length
577                                 * modifier, "a following `n' conversion
578                                 * specifier applies to a pointer to a
579                                 * signed integer type corresponding to
580                                 * size_t argument." (7.19.6.1, 7)
581                                 */
582                                 ssize_t* sizeptr = cast(ssize_t *)get_any_pointer(args);
583                                 *sizeptr = cast(ssize_t)(len + file_len);
584                                 break;
585                             case PRINT_C.INTMAX:
586                                 intmax_t* intmaxptr = cast(intmax_t *)get_any_pointer(args);
587                                 *intmaxptr = cast(intmax_t)(len + file_len);
588                                 break;
589                             case PRINT_C.PTRDIFF:
590                                 ptrdiff_t* ptrdiffptr = cast(ptrdiff_t *)get_any_pointer(args);
591                                 *ptrdiffptr = cast(ptrdiff_t)(len + file_len);
592                                 break;
593                             default:
594                                 int* intptr = cast(int *)get_any_pointer(args);
595                                 *intptr = cast(int)(len + file_len);
596                                 break;
597                         }
598                         break;
599                     case '%':    /* Print a "%" character verbatim. */
600                         OUTCHAR(ch);
601                         break;
602                     default:    /* Skip other characters. */
603                         break;
604                 }
605                 state = PRINT_S.DEFAULT;
606                 base = cflags = flags = width = 0;
607                 precision = -1;
608                 break;
609         }
610     }
611 
612 
613     static if (&file_func != &dummy_file_func) {
614         if (file != null && len > 0) {
615             file_func(file, cast(ubyte[])str[0..len]);
616             file_len += len;
617             len = file_len;
618         }
619     } else {
620         if (len < str.length)
621             str[len] = '\0';
622         else if (str.length > 0)
623             str[$ - 1] = '\0';
624     }
625         
626     if (overflow || len >= INT_MAX) {
627         //errno = overflow ? EOVERFLOW : ERANGE;
628         return -1;
629     }
630     return cast(int)len;
631 }
632 
633 void fmtstr(alias file_func)(char[] str, size_t *len, const(char)* value, int width, int precision, int flags, void* file, size_t* file_len) {
634     bool noprecision = (precision == -1);
635 
636     pragma(inline, true) void OUTCHAR(char ch) {
637         if (*len + 1 <= str.length)
638             str[*len] = ch;
639         (*len)++;
640         static if (&file_func != &dummy_file_func) {
641             if (file != null && *len == str.length) {
642                 file_func(file, cast(ubyte[])str);
643                 *file_len += *len;
644                 *len = 0;
645             }
646         }
647     }
648 
649     if (value == null)    /* We're forgiving. */
650         value = "(null)";
651 
652     /* If a precision was specified, don't read the string past it. */
653     int strln;
654     for (strln = 0; (noprecision || strln < precision) &&
655         value[strln] != '\0'; strln++)
656         continue;
657 
658     int padlen = width - strln;    /* Amount to pad. */
659     if (padlen < 0)
660         padlen = 0;
661     if (flags & PRINT_F.MINUS)    /* Left justify. */
662         padlen = -padlen;
663 
664     while (padlen > 0) {    /* Leading spaces. */
665         OUTCHAR(' ');
666         padlen--;
667     }
668     while ((noprecision || precision-- > 0) && *value != '\0') {
669         OUTCHAR(*value);
670         value++;
671     }
672     while (padlen < 0) {    /* Trailing spaces. */
673         OUTCHAR(' ');
674         padlen++;
675     }
676 }
677 
678 void fmtint(alias file_func)(char[] str, size_t *len, intmax_t value, int base, int width,
679     int precision, int flags, void* file, size_t* file_len) {
680     bool noprecision = (precision == -1);
681     
682     pragma(inline, true) void OUTCHAR(char ch) {
683         if (*len + 1 <= str.length)
684             str[*len] = ch;
685         (*len)++;
686         static if (&file_func != &dummy_file_func) {
687             if (file != null && *len == str.length) {
688                 file_func(file, cast(ubyte[])str);
689                 *file_len += *len;
690                 *len = 0;
691             }
692         }
693     }
694 
695     uintmax_t uvalue;
696     char sign = 0;
697     if (flags & PRINT_F.UNSIGNED)
698         uvalue = value;
699     else {
700         uvalue = (value >= 0) ? value : -value;
701         if (value < 0)
702             sign = '-';
703         else if (flags & PRINT_F.PLUS)    /* Do a sign. */
704             sign = '+';
705         else if (flags & PRINT_F.SPACE)
706             sign = ' ';
707     }
708 
709     char[MAX_CONVERT_LENGTH] iconvert;
710     int pos = convert(uvalue, iconvert, base,
711         flags & PRINT_F.UP);
712 
713     char hexprefix = 0;
714     if (flags & PRINT_F.NUM && uvalue != 0) {
715         /*
716         * C99 says: "The result is converted to an `alternative form'.
717         * For `o' conversion, it increases the precision, if and only
718         * if necessary, to force the first digit of the result to be a
719         * zero (if the value and precision are both 0, a single 0 is
720         * printed).  For `x' (or `X') conversion, a nonzero result has
721         * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
722         */
723         switch (base) {
724             case 8:
725                 if (precision <= pos)
726                     precision = pos + 1;
727                 break;
728             case 16:
729                 hexprefix = (flags & PRINT_F.UP) ? 'X' : 'x';
730                 break;
731             default:
732                 break;
733         }
734     }
735 
736     int separators = 0;
737     if (flags & PRINT_F.QUOTE)    /* Get the number of group separators we'll print. */
738         separators = getnumsep(pos);
739 
740     int zpadlen = precision - pos - separators;    /* Amount to zero pad. */
741     int spadlen = width                         /* Minimum field width. */
742         - separators                        /* Number of separators. */
743         - MAX(precision, pos)               /* Number of integer digits. */
744         - ((sign != 0) ? 1 : 0)             /* Will we print a sign? */
745         - ((hexprefix != 0) ? 2 : 0);       /* Will we print a prefix? */
746 
747     if (zpadlen < 0)
748         zpadlen = 0;
749     if (spadlen < 0)
750         spadlen = 0;
751 
752     /*
753     * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
754     * ignored.  For `d', `i', `o', `u', `x', and `X' conversions, if a
755     * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
756     */
757     if (flags & PRINT_F.MINUS)    /* Left justify. */
758         spadlen = -spadlen;
759     else if (flags & PRINT_F.ZERO && noprecision) {
760         zpadlen += spadlen;
761         spadlen = 0;
762     }
763     while (spadlen > 0) {    /* Leading spaces. */
764         OUTCHAR(' ');
765         spadlen--;
766     }
767     if (sign != 0)    /* Sign. */
768         OUTCHAR(sign);
769     if (hexprefix != 0) {    /* A "0x" or "0X" prefix. */
770         OUTCHAR('0');
771         OUTCHAR(hexprefix);
772     }
773     while (zpadlen > 0) {    /* Leading zeros. */
774         OUTCHAR('0');
775         zpadlen--;
776     }
777     while (pos > 0) {    /* The actual digits. */
778         pos--;
779         OUTCHAR(iconvert[pos]);
780         if (separators > 0 && pos > 0 && pos % 3 == 0)
781             OUTCHAR(',');
782     }
783     while (spadlen < 0) {    /* Trailing spaces. */
784         OUTCHAR(' ');
785         spadlen++;
786     }
787 }
788 
789 void fmtflt(alias file_func)(char[] str, size_t *len, real fvalue, int width,
790     int precision, int flags, int *overflow, void* file, size_t* file_len) {
791     
792     pragma(inline, true) void OUTCHAR(char ch) {
793         if (*len + 1 <= str.length)
794             str[*len] = ch;
795         (*len)++;
796         static if (&file_func != &dummy_file_func) {
797             if (file != null && *len == str.length) {
798                 file_func(file, cast(ubyte[])str);
799                 *file_len += *len;
800                 *len = 0;
801             }
802         }
803     }
804 
805     /*
806     * AIX' man page says the default is 0, but C99 and at least Solaris'
807     * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
808     * defaults to 6.
809     */
810     if (precision == -1)
811         precision = 6;
812 
813     char sign = 0;
814     if (fvalue < 0.0)
815         sign = '-';
816     else if (flags & PRINT_F.PLUS)    /* Do a sign. */
817         sign = '+';
818     else if (flags & PRINT_F.SPACE)
819         sign = ' ';
820 
821     const(char)* infnan = null;
822     if (ISNAN(fvalue))
823         infnan = (flags & PRINT_F.UP) ? "NAN" : "nan";
824     else if (ISINF(fvalue))
825         infnan = (flags & PRINT_F.UP) ? "INF" : "inf";
826 
827     int ipos = 0;
828     if (infnan != null) {
829         char[MAX_CONVERT_LENGTH] iconvert;
830         if (sign != 0)
831             iconvert[ipos++] = sign;
832         while (*infnan != '\0')
833             iconvert[ipos++] = *infnan++;
834         fmtstr!file_func(str, len, iconvert.ptr, width, ipos, flags, file, file_len);
835         return;
836     }
837 
838 
839     /* "%e" (or "%E") or "%g" (or "%G") conversion. */
840     int exponent = 0;
841     bool omitzeros = false;
842     bool estyle = (flags & PRINT_F.TYPE_E) != 0;
843     if (flags & PRINT_F.TYPE_E || flags & PRINT_F.TYPE_G) {
844         if (flags & PRINT_F.TYPE_G) {
845             /*
846             * For "%g" (and "%G") conversions, the precision
847             * specifies the number of significant digits, which
848             * includes the digits in the integer part.  The
849             * conversion will or will not be using "e-style" (like
850             * "%e" or "%E" conversions) depending on the precision
851             * and on the exponent.  However, the exponent can be
852             * affected by rounding the converted value, so we'll
853             * leave this decision for later.  Until then, we'll
854             * assume that we're going to do an "e-style" conversion
855             * (in order to get the exponent calculated).  For
856             * "e-style", the precision must be decremented by one.
857             */
858             precision--;
859             /*
860             * For "%g" (and "%G") conversions, trailing zeros are
861             * removed from the fractional portion of the result
862             * unless the "#" flag was specified.
863             */
864             if (!(flags & PRINT_F.NUM))
865                 omitzeros = true;
866         }
867         exponent = getexponent(fvalue);
868         estyle = true;
869     }
870 
871     again:
872     /*
873     * Sorry, we only support 9, 19, or 38 digits (that is, the number of
874     * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
875     * minus one) past the decimal point due to our conversion method.
876     */
877     switch (uintmax_t.sizeof) {
878         case 16:
879         if (precision > 38)
880             precision = 38;
881         break;
882         case 8:
883         if (precision > 19)
884             precision = 19;
885         break;
886         default:
887         if (precision > 9)
888             precision = 9;
889         break;
890     }
891 
892     real ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
893     if (estyle)    /* We want exactly one integer digit. */
894         ufvalue /= mypow10(exponent);
895 
896     uintmax_t intpart = _cast(ufvalue);
897     if (intpart == UINTMAX_MAX) {
898         *overflow = 1;
899         return;
900     }
901 
902     /*
903     * Factor of ten with the number of digits needed for the fractional
904     * part.  For example, if the precision is 3, the mask will be 1000.
905     */
906     uintmax_t mask = cast(uintmax_t)mypow10(precision);
907     /*
908     * We "cheat" by converting the fractional part to integer by
909     * multiplying by a factor of ten.
910     */
911     uintmax_t fracpart = myround(mask * (ufvalue - intpart));
912     if (fracpart >= mask) {
913         /*
914         * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
915         * (because precision = 3).  Now, myround(1000 * 0.99962) will
916         * return 1000.  So, the integer part must be incremented by one
917         * and the fractional part must be set to zero.
918         */
919         intpart++;
920         fracpart = 0;
921         if (estyle && intpart == 10) {
922             /*
923             * The value was rounded up to ten, but we only want one
924             * integer digit if using "e-style".  So, the integer
925             * part must be set to one and the exponent must be
926             * incremented by one.
927             */
928             intpart = 1;
929             exponent++;
930         }
931     }
932 
933     /*
934     * Now that we know the real exponent, we can check whether or not to
935     * use "e-style" for "%g" (and "%G") conversions.  If we don't need
936     * "e-style", the precision must be adjusted and the integer and
937     * fractional parts must be recalculated from the original value.
938     *
939     * C99 says: "Let P equal the precision if nonzero, 6 if the precision
940     * is omitted, or 1 if the precision is zero.  Then, if a conversion
941     * with style `E' would have an exponent of X:
942     *
943     * - if P > X >= -4, the conversion is with style `f' (or `F') and
944     *   precision P - (X + 1).
945     *
946     * - otherwise, the conversion is with style `e' (or `E') and precision
947     *   P - 1." (7.19.6.1, 8)
948     *
949     * Note that we had decremented the precision by one.
950     */
951     if (flags & PRINT_F.TYPE_G && estyle &&
952         precision + 1 > exponent && exponent >= -4) {
953         precision -= exponent;
954         estyle = false;
955         goto again;
956     }
957 
958     char[4] econvert;    /* "e-12" (without nul-termination). */
959     int epos = 0;
960     if (estyle) {
961         char esign = 0;
962         if (exponent < 0) {
963             exponent = -exponent;
964             esign = '-';
965         } else
966             esign = '+';
967 
968         /*
969         * Convert the exponent.  The econvert.sizeof is 4.  So, the
970         * econvert buffer can hold e.g. "e+99" and "e-99".  We don't
971         * support an exponent which contains more than two digits.
972         * Therefore, the following stores are safe.
973         */
974         epos = convert(exponent, econvert[0..2], 10, 0);
975         /*
976         * C99 says: "The exponent always contains at least two digits,
977         * and only as many more digits as necessary to represent the
978         * exponent." (7.19.6.1, 8)
979         */
980         if (epos == 1)
981             econvert[epos++] = '0';
982         econvert[epos++] = esign;
983         econvert[epos++] = (flags & PRINT_F.UP) ? 'E' : 'e';
984     }
985 
986     char[MAX_CONVERT_LENGTH] iconvert;
987     char[MAX_CONVERT_LENGTH] fconvert;
988     /* Convert the integer part and the fractional part. */
989     ipos = convert(intpart, iconvert, 10, 0);
990     int fpos = 0;
991     if (fracpart != 0)    /* convert() would return 1 if fracpart == 0. */
992         fpos = convert(fracpart, fconvert, 10, 0);
993 
994     int leadfraczeros = precision - fpos;
995 
996     int omitcount = 0;
997     if (omitzeros) {
998         if (fpos > 0)    /* Omit trailing fractional part zeros. */
999             while (omitcount < fpos && fconvert[omitcount] == '0')
1000                 omitcount++;
1001         else {    /* The fractional part is zero, omit it completely. */
1002             omitcount = precision;
1003             leadfraczeros = 0;
1004         }
1005         precision -= omitcount;
1006     }
1007 
1008     /*
1009     * Print a decimal point if either the fractional part is non-zero
1010     * and/or the "#" flag was specified.
1011     */
1012     bool emitpoint = false;
1013     if (precision > 0 || flags & PRINT_F.NUM)
1014         emitpoint = true;
1015     int separators = 0;
1016     if (flags & PRINT_F.QUOTE)    /* Get the number of group separators we'll print. */
1017         separators = getnumsep(ipos);
1018 
1019     int padlen = width                  /* Minimum field width. */
1020         - ipos                      /* Number of integer digits. */
1021         - epos                      /* Number of exponent characters. */
1022         - precision                 /* Number of fractional digits. */
1023         - separators                /* Number of group separators. */
1024         - (emitpoint ? 1 : 0)       /* Will we print a decimal point? */
1025         - ((sign != 0) ? 1 : 0);    /* Will we print a sign character? */
1026 
1027     if (padlen < 0)
1028         padlen = 0;
1029 
1030     /*
1031     * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
1032     * ignored." (7.19.6.1, 6)
1033     */
1034     if (flags & PRINT_F.MINUS)    /* Left justifty. */
1035         padlen = -padlen;
1036     else if (flags & PRINT_F.ZERO && padlen > 0) {
1037         if (sign != 0) {    /* Sign. */
1038             OUTCHAR(sign);
1039             sign = 0;
1040         }
1041         while (padlen > 0) {    /* Leading zeros. */
1042             OUTCHAR('0');
1043             padlen--;
1044         }
1045     }
1046     while (padlen > 0) {    /* Leading spaces. */
1047         OUTCHAR(' ');
1048         padlen--;
1049     }
1050     if (sign != 0)    /* Sign. */
1051         OUTCHAR(sign);
1052     while (ipos > 0) {    /* Integer part. */
1053         ipos--;
1054         OUTCHAR(iconvert[ipos]);
1055         if (separators > 0 && ipos > 0 && ipos % 3 == 0)
1056             OUTCHAR(',');
1057     }
1058     if (emitpoint) {    /* Decimal point. */
1059         OUTCHAR('.');
1060     }
1061     while (leadfraczeros > 0) {    /* Leading fractional part zeros. */
1062         OUTCHAR('0');
1063         leadfraczeros--;
1064     }
1065     while (fpos > omitcount) {    /* The remaining fractional part. */
1066         fpos--;
1067         OUTCHAR(fconvert[fpos]);
1068     }
1069     while (epos > 0) {    /* Exponent. */
1070         epos--;
1071         OUTCHAR(econvert[epos]);
1072     }
1073     while (padlen < 0) {    /* Trailing spaces. */
1074         OUTCHAR(' ');
1075         padlen++;
1076     }
1077 }
1078 
1079 int getnumsep(inout int digits) {
1080     inout const shared int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
1081     return separators;
1082 }
1083 
1084 int getexponent(real value) {
1085     real tmp = (value >= 0.0) ? value : -value;
1086     int exponent = 0;
1087 
1088     /*
1089     * We check for 99 > exponent > -99 in order to work around possible
1090     * endless loops which could happen (at least) in the second loop (at
1091     * least) if we're called with an infinite value.  However, we checked
1092     * for infinity before calling this function using our ISINF() macro, so
1093     * this might be somewhat paranoid.
1094     */
1095     while (tmp < 1.0 && tmp > 0.0 && --exponent > -99)
1096         tmp *= 10;
1097     while (tmp >= 10.0 && ++exponent < 99)
1098         tmp /= 10;
1099 
1100     return exponent;
1101 }
1102 
1103 int convert(uintmax_t value, char[] buf, int base, int caps) {
1104     const(char)* digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
1105     size_t pos = 0;
1106 
1107     /* We return an unterminated buffer with the digits in reverse order. */
1108     do {
1109         buf[pos++] = digits[cast(size_t)(value % base)];
1110         value /= base;
1111     } while (value != 0 && pos < buf.length);
1112 
1113     return cast(int)pos;
1114 }
1115 
1116 uintmax_t _cast(real value) {
1117     uintmax_t result;
1118 
1119     /*
1120     * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
1121     * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
1122     * it may be increased to the nearest higher representable value for the
1123     * comparison (cf. C99: 6.3.1.4, 2).  It might then equal the LDOUBLE
1124     * value although converting the latter to UINTMAX_MAX would overflow.
1125     */
1126     if (value >= UINTMAX_MAX)
1127         return UINTMAX_MAX;
1128 
1129     result = cast(uintmax_t)value;
1130     /*
1131     * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
1132     * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
1133     * the standard).  Sigh.
1134     */
1135     return (result <= value) ? result : result - 1;
1136 }
1137 
1138 uintmax_t myround(real value) {
1139     uintmax_t intpart = _cast(value);
1140 
1141     return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
1142 }
1143 
1144 real mypow10(int exponent) {
1145     real result = 1;
1146 
1147     while (exponent > 0) {
1148         result *= 10;
1149         exponent--;
1150     }
1151     while (exponent < 0) {
1152         result /= 10;
1153         exponent++;
1154     }
1155     return result;
1156 }
1157 
1158 public:
1159 
1160 int rpl_snprintf(A...)(char[] str, string format, A a) {
1161     mixin va_start!a;
1162 
1163     int len = rpl_vsnprintf!dummy_file_func(str, format, va_args);
1164     return len;
1165 }
1166 
1167 int rpl_fprintf(alias file_func, A...)(void* file, string format, A a) {
1168     mixin va_start!a;
1169 
1170     int len = rpl_vfprintf!file_func(file, format, va_args);
1171     return len;
1172 }
1173 
1174 int rpl_vfprintf(alias file_func)(void* file, string format, va_list ap) {
1175     if (file == null) {
1176         int len = rpl_vsnprintf!dummy_file_func(null, format, ap);
1177         return len;
1178     } else {
1179         char[1024] str = 0;
1180         int len = rpl_vsnprintf!file_func(str, format, ap, file);
1181         return len;
1182     }
1183 }
1184 
1185 int rpl_asprintf(alias alloc_func, A...)(char[]* ret, string format, A a) {
1186     mixin va_start!a;
1187 
1188     int len = rpl_vasprintf!alloc_func(ret, format, va_args);
1189     return len;
1190 }
1191 
1192 int rpl_vasprintf(alias alloc_func)(char[]* ret, string format, va_list ap) {
1193     va_list aq;
1194     size_t size;
1195     va_copy(aq, ap);
1196 
1197     int len = rpl_vsnprintf!dummy_file_func(null, format, aq);
1198     if (len < 0 || (*ret = (cast(char*)alloc_func(size = len + 1))[0..size]) is null)
1199         return -1;
1200     return rpl_vsnprintf!dummy_file_func(*ret, format, ap);
1201 }